home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 October / EnigmA AMIGA RUN 01 (1995)(G.R. Edizioni)(IT)[!][issue 1995-10][Aminet 7].iso / Aminet / comm / tcp / wu_ftpd_37_21.lha / wu-ftpd / src / ftpcmd.y < prev    next >
Text File  |  1994-09-16  |  43KB  |  1,589 lines

  1. /*
  2.  * Copyright (c) 1985, 1988 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *  This product includes software developed by the University of
  16.  *  California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  *
  33.  *  @(#)ftpcmd.y    5.24 (Berkeley) 2/25/91
  34.  */
  35.  
  36. /*
  37.  * Grammar for FTP commands.
  38.  * See RFC 959.
  39.  */
  40.  
  41. %{
  42.  
  43. #ifndef lint
  44. static char sccsid[] = "@(#)ftpcmd.y    5.24 (Berkeley) 2/25/91";
  45. #endif /* not lint */
  46.  
  47. #include "config.h"
  48. #include <sys/param.h>
  49. #include <sys/types.h>
  50. #include <sys/socket.h>
  51. #include <sys/stat.h>
  52. #include <netinet/in.h>
  53. #include <arpa/ftp.h>
  54. #include <stdio.h>
  55. #include <signal.h>
  56. #include <ctype.h>
  57. #include <pwd.h>
  58. #include <setjmp.h>
  59. #ifdef SYSSYSLOG
  60. #include <sys/syslog.h>
  61. #else
  62. #include <syslog.h>
  63. #endif
  64. #include <time.h>
  65. #include <string.h>
  66. #include <limits.h>
  67. #ifdef AMIGA
  68. #include "ftw.h"
  69. #else
  70. #include "support/ftw.h"
  71. #endif
  72. #include "extensions.h"
  73. #include "pathnames.h"
  74.  
  75. extern  int dolreplies;
  76. extern  char ls_long[50];
  77. extern  char ls_short[50];
  78. extern  struct sockaddr_in data_dest;
  79. extern  int logged_in;
  80. extern  struct passwd *pw;
  81. extern  int anonymous;
  82. extern  int logging;
  83. extern  int log_commands;
  84. extern  int type;
  85. extern  int form;
  86. extern  int debug;
  87. extern  int timeout;
  88. extern  int maxtimeout;
  89. extern  int pdata;
  90. extern  char hostname[], remotehost[];
  91. #ifdef SETPROCTITLE
  92. extern  char proctitle[];
  93. #endif
  94. extern  char *globerr;
  95. extern  int usedefault;
  96. extern  int transflag;
  97. extern  char tmpline[];
  98. extern  int data;
  99. char    **ftpglob();
  100. off_t   restart_point;
  101.  
  102. extern  char    *strunames[];
  103. extern  char    *typenames[];
  104. extern  char    *modenames[];
  105. extern  char    *formnames[];
  106.  
  107. static  int cmd_type;
  108. static  int cmd_form;
  109. static  int cmd_bytesz;
  110. char    cbuf[512];
  111. char    *fromname;
  112.  
  113.  
  114. struct tab {
  115.     char    *name;
  116.     short   token;
  117.     short   state;
  118.     short   implemented;    /* 1 if command is implemented */
  119.     char    *help;
  120. } cmdtab[], sitetab[];
  121.  
  122. #ifdef AMIGA
  123. extern int kludge;
  124. #endif
  125.  
  126. void print_groups();
  127.  
  128. static void toolong();
  129.  
  130. %}
  131.  
  132. %token
  133.     A   B   C   E   F   I
  134.     L   N   P   R   S   T
  135.  
  136.     SP  CRLF    COMMA   STRING  NUMBER
  137.  
  138.     USER    PASS    ACCT    REIN    QUIT    PORT
  139.     PASV    TYPE    STRU    MODE    RETR    STOR
  140.     APPE    MLFL    MAIL    MSND    MSOM    MSAM
  141.     MRSQ    MRCP    ALLO    REST    RNFR    RNTO
  142.     ABOR    DELE    CWD     LIST    NLST    SITE
  143.     STAT    HELP    NOOP    MKD     RMD     PWD
  144.     CDUP    STOU    SMNT    SYST    SIZE    MDTM
  145.  
  146.     UMASK   IDLE    CHMOD   GROUP   GPASS   NEWER
  147.     MINFO   INDEX   EXEC    ALIAS   CDPATH  GROUPS
  148.  
  149.     LEXERR
  150.  
  151. %union {
  152.     char    *String;
  153.     int     Number;
  154. }
  155.  
  156. %type <String>  STRING password pathname pathstring username
  157. %type <Number>  NUMBER byte_size check_login form_code mode_code octal_number
  158. %type <Number>  struct_code
  159.  
  160. %start  cmd_list
  161.  
  162. %%
  163.  
  164. cmd_list:   /* empty */
  165.     |   cmd_list cmd
  166.         = {
  167.             fromname = 0;
  168.             restart_point = 0;
  169.         }
  170.     |   cmd_list rcmd
  171.     ;
  172.  
  173. cmd:        USER SP username CRLF
  174.         = {
  175.             user($3);
  176.             if (log_commands) syslog(LOG_INFO, "USER %s", $3);
  177.             free($3);
  178.         }
  179.     |   PASS SP password CRLF
  180.         = {
  181.             if (log_commands)
  182.                 if (anonymous)
  183.                     syslog(LOG_INFO, "PASS %s", $3);
  184.                 else
  185.                     syslog(LOG_INFO, "PASS password");
  186.  
  187.             pass($3);
  188.             free($3);
  189.         }
  190.     |   PORT SP host_port CRLF
  191.         = {
  192.             if (log_commands) syslog(LOG_INFO, "PORT");
  193.             usedefault = 0;
  194.             if (pdata >= 0) {
  195.                 (void) close(pdata);
  196.                 pdata = -1;
  197.             }
  198.             reply(200, "PORT command successful.");
  199.         }
  200.     |   PASV CRLF
  201.         = {
  202.             if (log_commands) syslog(LOG_INFO, "PASV");
  203.             passive();
  204.         }
  205.     |   TYPE SP type_code CRLF
  206.         = {
  207.             if (log_commands) syslog(LOG_INFO, "TYPE %s", typenames[cmd_type]);
  208.             switch (cmd_type) {
  209.  
  210.             case TYPE_A:
  211.                 if (cmd_form == FORM_N) {
  212.                     reply(200, "Type set to A.");
  213.                     type = cmd_type;
  214.                     form = cmd_form;
  215.                 } else
  216.                     reply(504, "Form must be N.");
  217.                 break;
  218.  
  219.             case TYPE_E:
  220.                 reply(504, "Type E not implemented.");
  221.                 break;
  222.  
  223.             case TYPE_I:
  224.                 reply(200, "Type set to I.");
  225.                 type = cmd_type;
  226.                 break;
  227.  
  228.             case TYPE_L:
  229. #if NBBY == 8
  230.                 if (cmd_bytesz == 8) {
  231.                     reply(200,
  232.                         "Type set to L (byte size 8).");
  233.                     type = cmd_type;
  234.                 } else
  235.                     reply(504, "Byte size must be 8.");
  236. #else /* NBBY == 8 */
  237.                 UNIMPLEMENTED for NBBY != 8
  238. #endif /* NBBY == 8 */
  239.             }
  240.         }
  241.     |   STRU SP struct_code CRLF
  242.         = {
  243.             if (log_commands) syslog(LOG_INFO, "STRU %s", strunames[$3]);
  244.             switch ($3) {
  245.  
  246.             case STRU_F:
  247.                 reply(200, "STRU F ok.");
  248.                 break;
  249.  
  250.             default:
  251.                 reply(504, "Unimplemented STRU type.");
  252.             }
  253.         }
  254.     |   MODE SP mode_code CRLF
  255.         = {
  256.             if (log_commands) syslog(LOG_INFO, "MODE %s", modenames[$3]);
  257.             switch ($3) {
  258.  
  259.             case MODE_S:
  260.                 reply(200, "MODE S ok.");
  261.                 break;
  262.  
  263.             default:
  264.                 reply(502, "Unimplemented MODE type.");
  265.             }
  266.         }
  267.     |   ALLO SP NUMBER CRLF
  268.         = {
  269.             if (log_commands) syslog(LOG_INFO, "ALLO %d", $3);
  270.             reply(202, "ALLO command ignored.");
  271.         }
  272.     |   ALLO SP NUMBER SP R SP NUMBER CRLF
  273.         = {
  274.             if (log_commands) syslog(LOG_INFO, "ALLO %d R %d", $3, $7);
  275.             reply(202, "ALLO command ignored.");
  276.         }
  277.     |   RETR check_login SP pathname CRLF
  278.         = {
  279.             if (log_commands) syslog(LOG_INFO, "RETR %s", $4);
  280.             if ($2 && $4 != NULL)
  281.                 retrieve((char *) NULL, $4);
  282.             if ($4 != NULL)
  283.                 free($4);
  284.         }
  285.     |   STOR check_login SP pathname CRLF
  286.         = {
  287.             if (log_commands) syslog(LOG_INFO, "STOR %s", $4);
  288.             if ($2 && $4 != NULL)
  289.                 store($4, "w", 0);
  290.             if ($4 != NULL)
  291.                 free($4);
  292.         }
  293.     |   APPE check_login SP pathname CRLF
  294.         = {
  295.             if (log_commands) syslog(LOG_INFO, "APPE %s", $4);
  296.             if ($2 && $4 != NULL)
  297.                 store($4, "a", 0);
  298.             if ($4 != NULL)
  299.                 free($4);
  300.         }
  301.     |   NLST check_login CRLF
  302.         = {
  303.             if (log_commands) syslog(LOG_INFO, "NLST");
  304.             if ($2)
  305. #ifndef AMIGA
  306.                 send_file_list(".");
  307. #else
  308.                 send_file_list("");
  309. #endif
  310.         }
  311.     |   NLST check_login SP STRING CRLF
  312.         = {
  313.             if (log_commands) syslog(LOG_INFO, "NLST %s", $4);
  314.             if ($2 && $4) {
  315.                 send_file_list($4);
  316.                 free($4);
  317.             }
  318.         }
  319.     |   LIST check_login CRLF
  320.         = {
  321.             if (log_commands) syslog(LOG_INFO, "LIST");
  322.             if ($2)
  323.         if (anonymous && dolreplies)
  324.                 retrieve(ls_long, "");
  325.             else
  326.                 retrieve(ls_short, "");
  327.         }
  328.     |   LIST check_login SP pathname CRLF
  329.         = {
  330.             if (log_commands) syslog(LOG_INFO, "LIST %s", $4);
  331.             if ($2 && $4 != NULL)
  332.         if (anonymous && dolreplies)
  333.                 retrieve(ls_long, $4);
  334.             else
  335.                 retrieve(ls_short, $4);
  336.             if ($4 != NULL)
  337.                 free($4);
  338.         }
  339.     |   STAT check_login SP pathname CRLF
  340.         = {
  341.             if (log_commands) syslog(LOG_INFO, "STAT %s", $4);
  342.             if ($2 && $4 != NULL)
  343.                 statfilecmd($4);
  344.             if ($4 != NULL)
  345.                 free($4);
  346.         }
  347.     |   STAT CRLF
  348.         = {
  349.             if (log_commands) syslog(LOG_INFO, "STAT");
  350.             statcmd();
  351.         }
  352.     |   DELE check_login SP pathname CRLF
  353.         = {
  354.             if (log_commands) syslog(LOG_INFO, "DELE %s", $4);
  355.             if ($2 && $4 != NULL)
  356.                 delete($4);
  357.             if ($4 != NULL)
  358.                 free($4);
  359.         }
  360.     |   RNTO SP pathname CRLF
  361.         = {
  362.             if (log_commands) syslog(LOG_INFO, "RNTO %s", $3);
  363.             if (fromname) {
  364.                 renamecmd(fromname, $3);
  365.                 free(fromname);
  366.                 fromname = (char *) NULL;
  367.             } else {
  368.                 reply(503, "Bad sequence of commands.");
  369.             }
  370.             free($3);
  371.         }
  372.     |   ABOR CRLF
  373.         = {
  374.             if (log_commands) syslog(LOG_INFO, "ABOR");
  375.             reply(225, "ABOR command successful.");
  376.         }
  377.     |   CWD check_login CRLF
  378.         = {
  379.             if (log_commands) syslog(LOG_INFO, "CWD");
  380.             if ($2)
  381.                 cwd(pw->pw_dir);
  382.         }
  383.     |   CWD check_login SP pathname CRLF
  384.         = {
  385.             if (log_commands) syslog(LOG_INFO, "CWD %s", $4);
  386.             if ($2 && $4 != NULL)
  387.                 cwd($4);
  388.             if ($4 != NULL)
  389.                 free($4);
  390.         }
  391.     |   HELP CRLF
  392.         = {
  393.             if (log_commands) syslog(LOG_INFO, "HELP");
  394.             help(cmdtab, (char *) NULL);
  395.         }
  396.     |   HELP SP STRING CRLF
  397.         = {
  398.             register char *cp = (char *)$3;
  399.  
  400.             if (log_commands) syslog(LOG_INFO, "HELP %s", $3);
  401.             if (strncasecmp(cp, "SITE", 4) == 0) {
  402.                 cp = (char *)$3 + 4;
  403.                 if (*cp == ' ')
  404.                     cp++;
  405.                 if (*cp)
  406.                     help(sitetab, cp);
  407.                 else
  408.                     help(sitetab, (char *) NULL);
  409.             } else
  410.                 help(cmdtab, $3);
  411.         }
  412.     |   NOOP CRLF
  413.         = {
  414.             if (log_commands) syslog(LOG_INFO, "NOOP");
  415.             reply(200, "NOOP command successful.");
  416.         }
  417.     |   MKD check_login SP pathname CRLF
  418.         = {
  419.             if (log_commands) syslog(LOG_INFO, "MKD %s", $4);
  420.             if ($2 && $4 != NULL)
  421.                 makedir($4);
  422.             if ($4 != NULL)
  423.                 free($4);
  424.         }
  425.     |   RMD check_login SP pathname CRLF
  426.         = {
  427.             if (log_commands) syslog(LOG_INFO, "RMD %s", $4);
  428.             if ($2 && $4 != NULL)
  429.                 removedir($4);
  430.             if ($4 != NULL)
  431.                 free($4);
  432.         }
  433.     |   PWD check_login CRLF
  434.         = {
  435.             if (log_commands) syslog(LOG_INFO, "PWD");
  436.             if ($2)
  437.                 pwd();
  438.         }
  439.     |   CDUP check_login CRLF
  440.         = {
  441.             if (log_commands) syslog(LOG_INFO, "CDUP");
  442.             if ($2)
  443.                 cwd("..");
  444.         }
  445.     |   SITE SP HELP CRLF
  446.         = {
  447.             if (log_commands) syslog(LOG_INFO, "SITE HELP");
  448.             help(sitetab, (char *) NULL);
  449.         }
  450.     |   SITE SP HELP SP STRING CRLF
  451.         = {
  452.             if (log_commands) syslog(LOG_INFO, "SITE HELP %s", $5);
  453.             help(sitetab, $5);
  454.         }
  455.     |   SITE SP UMASK check_login CRLF
  456.         = {
  457.             mode_t oldmask;
  458.  
  459.             if (log_commands) syslog(LOG_INFO, "SITE UMASK");
  460.             if ($4) {
  461.                 oldmask = umask(0);
  462.                 (void) umask(oldmask);
  463.                 reply(200, "Current UMASK is %03o", oldmask);
  464.             }
  465.         }
  466.     |   SITE SP UMASK check_login SP octal_number CRLF
  467.         = {
  468.             mode_t oldmask;
  469.             struct aclmember *entry = NULL;
  470.             int ok = 1;
  471.  
  472.             if (log_commands) syslog(LOG_INFO, "SITE UMASK %d", $6);
  473.             if ($4) {
  474.                 /* check for umask permission */
  475.                 while (getaclentry("umask", &entry) && ARG0 && ARG1 != NULL) {
  476.                     if (type_match(ARG1)) 
  477.                         if (*ARG0 == 'n')  ok = 0;
  478.                 }
  479.                 if (ok) {
  480.                     if (($6 == -1) || ($6 > 0777)) {
  481.                         reply(501, "Bad UMASK value");
  482.                     } else {
  483.                         oldmask = umask((mode_t)$6);
  484.                         reply(200, "UMASK set to %03o (was %03o)", $6, oldmask);
  485.                     }
  486.                 } else 
  487.                     reply(553, "Permission denied. (umask)");
  488.             }
  489.         }
  490.     |   SITE SP CHMOD check_login SP octal_number SP pathname CRLF
  491.         = {
  492.             struct aclmember *entry = NULL;
  493.             int ok = 1;
  494.  
  495.             if (log_commands) syslog(LOG_INFO, "SITE CHMOD %d %s", $6, $8);
  496.             if ($4 && $6 && $8) {
  497.                 /* check for chmod permission */
  498.                 while (getaclentry("chmod", &entry) && ARG0 && ARG1 != NULL) {
  499.                     if (type_match(ARG1)) 
  500.                         if (*ARG0 == 'n')  ok = 0;
  501.                 }
  502.                 if (ok) {
  503.                     if ($6 > 0777)
  504.                         reply(501, 
  505.                             "CHMOD: Mode value must be between 0 and 0777");
  506.                     else if (chmod($8, (mode_t) $6) < 0)
  507.                         perror_reply(550, $8);
  508.                     else
  509.                         reply(200, "CHMOD command successful.");
  510.                     free($8);
  511.                 } else
  512.                     reply(553, "Permission denied. (chmod)");
  513.             }
  514.         }
  515.     |   SITE SP IDLE CRLF
  516.         = {
  517.             if (log_commands) syslog(LOG_INFO, "SITE IDLE");
  518.             reply(200,
  519.                 "Current IDLE time limit is %d seconds; max %d",
  520.                 timeout, maxtimeout);
  521.         }
  522.     |   SITE SP IDLE SP NUMBER CRLF
  523.         = {
  524.             if (log_commands) syslog(LOG_INFO, "SITE IDLE %d", $5);
  525.             if ($5 < 30 || $5 > maxtimeout) {
  526.                 reply(501,
  527.             "Maximum IDLE time must be between 30 and %d seconds",
  528.                     maxtimeout);
  529.             } else {
  530.                 timeout = $5;
  531.                 (void) alarm((unsigned) timeout);
  532.                 reply(200, "Maximum IDLE time set to %d seconds", timeout);
  533.             }
  534.         }
  535.     |   SITE SP GROUP check_login SP username CRLF
  536.         = {
  537. #ifndef NO_PRIVATE
  538.             if (log_commands) syslog(LOG_INFO, "SITE GROUP %s", $6);
  539.             if ($4 && $6) priv_group($6);
  540.             free($6);
  541. #endif /* !NO_PRIVATE */
  542.         }
  543.     |   SITE SP GPASS check_login SP password CRLF
  544.         = {
  545. #ifndef NO_PRIVATE
  546.             if (log_commands) syslog(LOG_INFO, "SITE GPASS password");
  547.             if ($4 && $6) priv_gpass($6);
  548.             free($6);
  549. #endif /* !NO_PRIVATE */
  550.         }
  551.     |   SITE SP NEWER check_login SP STRING CRLF
  552.         = {
  553. #ifndef AMIGA
  554.             if ($4 && $6) newer($6, ".", 0);
  555. #else
  556.             if ($4 && $6) newer($6, "", 0);
  557. #endif
  558.             free($6);
  559.         }
  560.     |   SITE SP NEWER check_login SP STRING SP pathname CRLF
  561.         = {
  562.             if ($4 && $6 && $8) newer($6, $8, 0);
  563.             free($6);
  564.             free($8);
  565.         }
  566.     |   SITE SP MINFO check_login SP STRING SP pathname CRLF
  567.         = {
  568.             if ($4 && $6 && $8) newer($6, $8, 1);
  569.             free($6);
  570.             free($8);
  571.         }
  572.     |   SITE SP INDEX check_login SP STRING CRLF
  573.         = {
  574.             /* this is just for backward compatibility since we
  575.              * thought of INDEX before we thought of EXEC
  576.              */
  577.             if ($4 != 0 && $6 != NULL) {
  578.                 char buf[MAXPATHLEN];
  579.                 if (strlen($6) + 7 <= sizeof(buf)) {
  580.                     sprintf(buf, "index %s", (char*)$6);
  581.                     (void) site_exec(buf);
  582.                 }
  583.             }
  584.         }
  585.     |   SITE SP EXEC check_login SP STRING CRLF
  586.         = {
  587.             if ($4 != 0 && $6 != NULL) {
  588.                 (void) site_exec((char*)$6);
  589.             }
  590.         }
  591.     |   STOU check_login SP pathname CRLF
  592.         = {
  593.             if (log_commands) syslog(LOG_INFO, "STOU %s", $4);
  594.             if ($2 && $4) {
  595.                 store($4, "w", 1);
  596.                 free($4);
  597.             }
  598.         }
  599.     |   SYST CRLF
  600.         = {
  601.             if (log_commands) syslog(LOG_INFO, "SYST");
  602. #ifdef unix
  603. #ifdef BSD
  604.             reply(215, "UNIX Type: L%d Version: BSD-%d",
  605.                 NBBY, BSD);
  606. #else  /* BSD */
  607.             reply(215, "UNIX Type: L%d", NBBY);
  608. #endif /* BSD */
  609. #else  /* unix */
  610. #ifdef AMIGA
  611.             reply(215, "AMIGA Type: L%d", NBBY);
  612. #else
  613.             reply(215, "UNKNOWN Type: L%d", NBBY);
  614. #endif
  615. #endif /* unix */
  616.         }
  617.  
  618.         /*
  619.          * SIZE is not in RFC959, but Postel has blessed it and
  620.          * it will be in the updated RFC.
  621.          *
  622.          * Return size of file in a format suitable for
  623.          * using with RESTART (we just count bytes).
  624.          */
  625.     |   SIZE check_login SP pathname CRLF
  626.         = {
  627.             if (log_commands) syslog(LOG_INFO, "SIZE %s", $4);
  628.             if ($2 && $4) {
  629.                 sizecmd($4);
  630.                 free($4);
  631.             }
  632.         }
  633.  
  634.         /*
  635.          * MDTM is not in RFC959, but Postel has blessed it and
  636.          * it will be in the updated RFC.
  637.          *
  638.          * Return modification time of file as an ISO 3307
  639.          * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
  640.          * where xxx is the fractional second (of any precision,
  641.          * not necessarily 3 digits)
  642.          */
  643.     |   MDTM check_login SP pathname CRLF
  644.         = {
  645.             if (log_commands) syslog(LOG_INFO, "MDTM %s", $4);
  646.             if ($2 && $4) {
  647.                 struct stat stbuf;
  648.  
  649.                 if (stat($4, &stbuf) < 0)
  650.                     perror_reply(550, $4);
  651.                 else if (
  652. #ifdef AMIGA
  653.                          !kludge &&
  654. #endif
  655.                          (stbuf.st_mode&S_IFMT) != S_IFREG) {
  656.                     reply(550, "%s: not a plain file.",
  657.                         $4);
  658.                 } else {
  659.                     register struct tm *t;
  660.                     struct tm *gmtime();
  661.                     t = gmtime(&stbuf.st_mtime);
  662.                     reply(213,
  663.                         "19%02d%02d%02d%02d%02d%02d",
  664.                         t->tm_year, t->tm_mon+1, t->tm_mday,
  665.                         t->tm_hour, t->tm_min, t->tm_sec);
  666.                 }
  667.                 free($4);
  668.             }
  669.         }
  670.     |   QUIT CRLF
  671.         = {
  672.             if (log_commands) syslog(LOG_INFO, "QUIT");
  673.             reply(221, "Goodbye.");
  674.             dologout(0);
  675.         }
  676.     |   error CRLF
  677.         = {
  678.             yyerrok;
  679.         }
  680.     ;
  681. rcmd:       RNFR check_login SP pathname CRLF
  682.         = {
  683.             char *renamefrom();
  684.  
  685.             if (log_commands) syslog(LOG_INFO, "RNFR %s", $4);
  686.             restart_point = (off_t) 0;
  687.             if ($2 && $4) {
  688.                 fromname = renamefrom($4);
  689.                 if (fromname == 0 && $4) {
  690.                     free($4);
  691.                 }
  692.             }
  693.         }
  694.     |   REST SP byte_size CRLF
  695.         = {
  696.             long atol();
  697.  
  698.             fromname = 0;
  699.             restart_point = $3;
  700.             if (log_commands) syslog(LOG_INFO, "REST %d", restart_point);
  701.             reply(350, "Restarting at %ld. %s", restart_point,
  702.                 "Send STORE or RETRIEVE to initiate transfer.");
  703.         }
  704.  
  705.     |   SITE SP ALIAS CRLF
  706.         = {
  707.            if (log_commands) syslog(LOG_INFO, "SITE ALIAS");
  708.            alias ((char *)NULL);
  709.         }
  710.     |   SITE SP ALIAS SP STRING CRLF
  711.         = {
  712.            if (log_commands) syslog(LOG_INFO, "SITE ALIAS %s", $5);
  713.            alias ($5);
  714.         }
  715.     |   SITE SP GROUPS CRLF
  716.         = {
  717.            if (log_commands) syslog(LOG_INFO, "SITE GROUPS");
  718.            print_groups () ;
  719.         }
  720.     |   SITE SP CDPATH CRLF
  721.         = {
  722.            if (log_commands) syslog(LOG_INFO, "SITE CDPATH");
  723.            cdpath () ;
  724.         }
  725.     ;
  726.         
  727. username:   STRING
  728.     ;
  729.  
  730. password:   /* empty */
  731.         = {
  732.             $$ = malloc(1);
  733.             $$[0] = '\0';
  734.         }
  735.     |   STRING
  736.     ;
  737.  
  738. byte_size:  NUMBER
  739.     ;
  740.  
  741. host_port:  NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 
  742.         NUMBER COMMA NUMBER
  743.         = {
  744.             register char *a, *p;
  745.  
  746.             a = (char *)&data_dest.sin_addr;
  747.             a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
  748.             p = (char *)&data_dest.sin_port;
  749.             p[0] = $9; p[1] = $11;
  750.             data_dest.sin_family = AF_INET;
  751.         }
  752.     ;
  753.  
  754. form_code:  N
  755.     = {
  756.         $$ = FORM_N;
  757.     }
  758.     |   T
  759.     = {
  760.         $$ = FORM_T;
  761.     }
  762.     |   C
  763.     = {
  764.         $$ = FORM_C;
  765.     }
  766.     ;
  767.  
  768. type_code:  A
  769.     = {
  770.         cmd_type = TYPE_A;
  771.         cmd_form = FORM_N;
  772.     }
  773.     |   A SP form_code
  774.     = {
  775.         cmd_type = TYPE_A;
  776.         cmd_form = $3;
  777.     }
  778.     |   E
  779.     = {
  780.         cmd_type = TYPE_E;
  781.         cmd_form = FORM_N;
  782.     }
  783.     |   E SP form_code
  784.     = {
  785.         cmd_type = TYPE_E;
  786.         cmd_form = $3;
  787.     }
  788.     |   I
  789.     = {
  790.         cmd_type = TYPE_I;
  791.     }
  792.     |   L
  793.     = {
  794.         cmd_type = TYPE_L;
  795.         cmd_bytesz = NBBY;
  796.     }
  797.     |   L SP byte_size
  798.     = {
  799.         cmd_type = TYPE_L;
  800.         cmd_bytesz = $3;
  801.     }
  802.     /* this is for a bug in the BBN ftp */
  803.     |   L byte_size
  804.     = {
  805.         cmd_type = TYPE_L;
  806.         cmd_bytesz = $2;
  807.     }
  808.     ;
  809.  
  810. struct_code:    F
  811.     = {
  812.         $$ = STRU_F;
  813.     }
  814.     |   R
  815.     = {
  816.         $$ = STRU_R;
  817.     }
  818.     |   P
  819.     = {
  820.         $$ = STRU_P;
  821.     }
  822.     ;
  823.  
  824. mode_code:  S
  825.     = {
  826.         $$ = MODE_S;
  827.     }
  828.     |   B
  829.     = {
  830.         $$ = MODE_B;
  831.     }
  832.     |   C
  833.     = {
  834.         $$ = MODE_C;
  835.     }
  836.     ;
  837.  
  838. pathname:   pathstring
  839.     = {
  840.         /*
  841.          * Problem: this production is used for all pathname
  842.          * processing, but only gives a 550 error reply.
  843.          * This is a valid reply in some cases but not in others.
  844.          */
  845.         if (logged_in && $1 && strncmp($1, "~", 1) == 0) {
  846.             $$ = *ftpglob($1);
  847.             if (globerr) {
  848.                 reply(550, globerr);
  849.                 $$ = NULL;
  850.             }
  851.             free($1);
  852.         } else
  853.             $$ = $1;
  854.     }
  855.     ;
  856.  
  857. pathstring: STRING
  858.     ;
  859.  
  860. octal_number:   NUMBER
  861.     = {
  862.         register int ret, dec, multby, digit;
  863.  
  864.         /*
  865.          * Convert a number that was read as decimal number
  866.          * to what it would be if it had been read as octal.
  867.          */
  868.         dec = $1;
  869.         multby = 1;
  870.         ret = 0;
  871.         while (dec) {
  872.             digit = dec%10;
  873.             if (digit > 7) {
  874.                 ret = -1;
  875.                 break;
  876.             }
  877.             ret += digit * multby;
  878.             multby *= 8;
  879.             dec /= 10;
  880.         }
  881.         $$ = ret;
  882.     }
  883.     ;
  884.  
  885. check_login:    /* empty */
  886.     = {
  887.         if (logged_in)
  888.             $$ = 1;
  889.         else {
  890.             if (log_commands) syslog(LOG_INFO, "cmd failure - not logged in");
  891.             reply(530, "Please login with USER and PASS.");
  892.             $$ = 0;
  893.         }
  894.     }
  895.     ;
  896.  
  897. %%
  898.  
  899. extern jmp_buf errcatch;
  900.  
  901. #define CMD 0   /* beginning of command */
  902. #define ARGS    1   /* expect miscellaneous arguments */
  903. #define STR1    2   /* expect SP followed by STRING */
  904. #define STR2    3   /* expect STRING */
  905. #define OSTR    4   /* optional SP then STRING */
  906. #define ZSTR1   5   /* SP then optional STRING */
  907. #define ZSTR2   6   /* optional STRING after SP */
  908. #define SITECMD 7   /* SITE command */
  909. #define NSTR    8   /* Number followed by a string */
  910. #define STR3    9   /* expect STRING followed by optional SP then STRING */
  911.  
  912. struct tab cmdtab[] = {     /* In order defined in RFC 765 */
  913.     { "USER", USER, STR1, 1,    "<sp> username" },
  914.     { "PASS", PASS, ZSTR1, 1,   "<sp> password" },
  915.     { "ACCT", ACCT, STR1, 0,    "(specify account)" },
  916.     { "SMNT", SMNT, ARGS, 0,    "(structure mount)" },
  917.     { "REIN", REIN, ARGS, 0,    "(reinitialize server state)" },
  918.     { "QUIT", QUIT, ARGS, 1,    "(terminate service)", },
  919.     { "PORT", PORT, ARGS, 1,    "<sp> b0, b1, b2, b3, b4" },
  920.     { "PASV", PASV, ARGS, 1,    "(set server in passive mode)" },
  921.     { "TYPE", TYPE, ARGS, 1,    "<sp> [ A | E | I | L ]" },
  922.     { "STRU", STRU, ARGS, 1,    "(specify file structure)" },
  923.     { "MODE", MODE, ARGS, 1,    "(specify transfer mode)" },
  924.     { "RETR", RETR, STR1, 1,    "<sp> file-name" },
  925.     { "STOR", STOR, STR1, 1,    "<sp> file-name" },
  926.     { "APPE", APPE, STR1, 1,    "<sp> file-name" },
  927.     { "MLFL", MLFL, OSTR, 0,    "(mail file)" },
  928.     { "MAIL", MAIL, OSTR, 0,    "(mail to user)" },
  929.     { "MSND", MSND, OSTR, 0,    "(mail send to terminal)" },
  930.     { "MSOM", MSOM, OSTR, 0,    "(mail send to terminal or mailbox)" },
  931.     { "MSAM", MSAM, OSTR, 0,    "(mail send to terminal and mailbox)" },
  932.     { "MRSQ", MRSQ, OSTR, 0,    "(mail recipient scheme question)" },
  933.     { "MRCP", MRCP, STR1, 0,    "(mail recipient)" },
  934.     { "ALLO", ALLO, ARGS, 1,    "allocate storage (vacuously)" },
  935.     { "REST", REST, ARGS, 1,    "(restart command)" },
  936.     { "RNFR", RNFR, STR1, 1,    "<sp> file-name" },
  937.     { "RNTO", RNTO, STR1, 1,    "<sp> file-name" },
  938.     { "ABOR", ABOR, ARGS, 1,    "(abort operation)" },
  939.     { "DELE", DELE, STR1, 1,    "<sp> file-name" },
  940.     { "CWD",  CWD,  OSTR, 1,    "[ <sp> directory-name ]" },
  941.     { "XCWD", CWD,  OSTR, 1,    "[ <sp> directory-name ]" },
  942.     { "LIST", LIST, OSTR, 1,    "[ <sp> path-name ]" },
  943.     { "NLST", NLST, OSTR, 1,    "[ <sp> path-name ]" },
  944.     { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
  945.     { "SYST", SYST, ARGS, 1,    "(get type of operating system)" },
  946.     { "STAT", STAT, OSTR, 1,    "[ <sp> path-name ]" },
  947.     { "HELP", HELP, OSTR, 1,    "[ <sp> <string> ]" },
  948.     { "NOOP", NOOP, ARGS, 1,    "" },
  949.     { "MKD",  MKD,  STR1, 1,    "<sp> path-name" },
  950.     { "XMKD", MKD,  STR1, 1,    "<sp> path-name" },
  951.     { "RMD",  RMD,  STR1, 1,    "<sp> path-name" },
  952.     { "XRMD", RMD,  STR1, 1,    "<sp> path-name" },
  953.     { "PWD",  PWD,  ARGS, 1,    "(return current directory)" },
  954.     { "XPWD", PWD,  ARGS, 1,    "(return current directory)" },
  955.     { "CDUP", CDUP, ARGS, 1,    "(change to parent directory)" },
  956.     { "XCUP", CDUP, ARGS, 1,    "(change to parent directory)" },
  957.     { "STOU", STOU, STR1, 1,    "<sp> file-name" },
  958.     { "SIZE", SIZE, OSTR, 1,    "<sp> path-name" },
  959.     { "MDTM", MDTM, OSTR, 1,    "<sp> path-name" },
  960.     { NULL,   0,    0,    0,    0 }
  961. };
  962.  
  963. struct tab sitetab[] = {
  964.     { "UMASK", UMASK, ARGS, 1,  "[ <sp> umask ]" },
  965.     { "IDLE",  IDLE,  ARGS, 1,  "[ <sp> maximum-idle-time ]" },
  966.     { "CHMOD", CHMOD, NSTR, 1,  "<sp> mode <sp> file-name" },
  967.     { "HELP",  HELP,  OSTR, 1,  "[ <sp> <string> ]" },
  968.     { "GROUP", GROUP, STR1, 1,  "<sp> access-group" },
  969.     { "GPASS", GPASS, STR1, 1,  "<sp> access-password" },
  970.     { "NEWER", NEWER, STR3, 1,  "<sp> YYYYMMDDHHMMSS [ <sp> path-name ]" },
  971.     { "MINFO", MINFO, STR3, 1,  "<sp> YYYYMMDDHHMMSS [ <sp> path-name ]" },
  972.     { "INDEX", INDEX, STR1, 1,  "<sp> pattern" },
  973.     { "EXEC",  EXEC,  STR1, 1,  "<sp> command [ <sp> arguments ]" },
  974.     { "ALIAS", ALIAS, OSTR, 1,  "[ <sp> alias ] " },
  975.     { "CDPATH", CDPATH, OSTR, 1,  "[ <sp> ] " },
  976.     { "GROUPS", GROUPS, OSTR, 1,  "[ <sp> ] " },
  977.     { NULL,    0,     0,    0,  0 }
  978. };
  979.  
  980. struct tab *
  981. lookup(p, cmd)
  982.     register struct tab *p;
  983.     char *cmd;
  984. {
  985.  
  986.     for (; p->name != NULL; p++)
  987.         if (strcmp(cmd, p->name) == 0)
  988.             return (p);
  989.     return (0);
  990. }
  991.  
  992. #include <arpa/telnet.h>
  993.  
  994. /*
  995.  * getline - a hacked up version of fgets to ignore TELNET escape codes.
  996.  */
  997. char *
  998. getline(s, n, iop)
  999.     char *s;
  1000.     register FILE *iop;
  1001. {
  1002.     register c;
  1003.     register char *cs;
  1004.  
  1005.     cs = s;
  1006. /* tmpline may contain saved command from urgent mode interruption */
  1007.     for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
  1008.         *cs++ = tmpline[c];
  1009.         if (tmpline[c] == '\n') {
  1010.             *cs++ = '\0';
  1011.             if (debug)
  1012.                 syslog(LOG_DEBUG, "command: %s", s);
  1013.             tmpline[0] = '\0';
  1014.             return(s);
  1015.         }
  1016.         if (c == 0)
  1017.             tmpline[0] = '\0';
  1018.     }
  1019.     while ((c = getc(iop)) != EOF) {
  1020.         c &= 0377;
  1021.         if (c == IAC) {
  1022.             if ((c = getc(iop)) != EOF) {
  1023.             c &= 0377;
  1024.             switch (c) {
  1025.             case WILL:
  1026.             case WONT:
  1027.                 c = getc(iop);
  1028.                 printf("%c%c%c", IAC, DONT, 0377&c);
  1029.                 (void) fflush(stdout);
  1030.                 continue;
  1031.             case DO:
  1032.             case DONT:
  1033.                 c = getc(iop);
  1034.                 printf("%c%c%c", IAC, WONT, 0377&c);
  1035.                 (void) fflush(stdout);
  1036.                 continue;
  1037.             case IAC:
  1038.                 break;
  1039.             default:
  1040.                 continue;   /* ignore command */
  1041.             }
  1042.             }
  1043.         }
  1044.         *cs++ = c;
  1045.         if (--n <= 0 || c == '\n')
  1046.             break;
  1047.     }
  1048.     if (c == EOF && cs == s)
  1049.         return (NULL);
  1050.     *cs++ = '\0';
  1051.     if (debug)
  1052.         syslog(LOG_DEBUG, "command: %s", s);
  1053.     return (s);
  1054. }
  1055.  
  1056. static void
  1057. toolong()
  1058. {
  1059.     time_t now;
  1060.  
  1061.     reply(421,
  1062.       "Timeout (%d seconds): closing control connection.", timeout);
  1063.     (void) time(&now);
  1064.     if (logging) {
  1065.         syslog(LOG_INFO,
  1066.             "User %s timed out after %d seconds at %.24s",
  1067.             (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
  1068.     }
  1069.     dologout(1);
  1070. }
  1071.  
  1072. yylex()
  1073. {
  1074.     static int cpos, state;
  1075.     register char *cp, *cp2;
  1076.     register struct tab *p;
  1077.     int n;
  1078.     char c, *copy();
  1079.  
  1080.     for (;;) {
  1081.         switch (state) {
  1082.  
  1083.         case CMD:
  1084. #ifdef AMIGA
  1085.             /* Free all memory that was allocated for
  1086.              * Unix<->Amiga path conversion.
  1087.              */
  1088.             FreeAmigaPath();
  1089. #endif
  1090.             (void) signal(SIGALRM, toolong);
  1091.             (void) alarm((unsigned) timeout);
  1092.             if (is_shutdown(!logged_in) != 0) {
  1093.                 reply(221, "Server shutting down.  Goodbye.");
  1094.                 dologout(0);
  1095.             }
  1096. #ifdef SETPROCTITLE
  1097.             setproctitle("%s: IDLE", proctitle);
  1098. #endif
  1099.             if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
  1100. #ifdef AMIGA
  1101.                 chkabort();
  1102. #endif
  1103.                 reply(221, "You could at least say goodbye.");
  1104.                 dologout(0);
  1105.             }
  1106.             (void) alarm(0);
  1107. #ifdef SETPROCTITLE
  1108.             if (strncasecmp(cbuf, "PASS", 4) != 0 &&
  1109.                 strncasecmp(cbuf, "SITE GPASS", 10) != 0)
  1110.                 setproctitle("%s: %s", proctitle, cbuf);
  1111. #endif /* SETPROCTITLE */
  1112.             if ((cp = strchr(cbuf, '\r'))) {
  1113.                 *cp++ = '\n';
  1114.                 *cp = '\0';
  1115.             }
  1116.             if ((cp = strpbrk(cbuf, " \n")))
  1117.                 cpos = cp - cbuf;
  1118.             if (cpos == 0)
  1119.                 cpos = 4;
  1120.             c = cbuf[cpos];
  1121.             cbuf[cpos] = '\0';
  1122.             upper(cbuf);
  1123.             p = lookup(cmdtab, cbuf);
  1124.             cbuf[cpos] = c;
  1125.             if (p != 0) {
  1126.                 if (p->implemented == 0) {
  1127.                     nack(p->name);
  1128.                     longjmp(errcatch,0);
  1129.                     /* NOTREACHED */
  1130.                 }
  1131.                 state = p->state;
  1132.                 yylval.String = p->name;
  1133.                 return (p->token);
  1134.             }
  1135.             break;
  1136.  
  1137.         case SITECMD:
  1138.             if (cbuf[cpos] == ' ') {
  1139.                 cpos++;
  1140.                 return (SP);
  1141.             }
  1142.             cp = &cbuf[cpos];
  1143.             if ((cp2 = strpbrk(cp, " \n")))
  1144.                 cpos = cp2 - cbuf;
  1145.             c = cbuf[cpos];
  1146.             cbuf[cpos] = '\0';
  1147.             upper(cp);
  1148.             p = lookup(sitetab, cp);
  1149.             cbuf[cpos] = c;
  1150.             if (p != 0) {
  1151.                 if (p->implemented == 0) {
  1152.                     state = CMD;
  1153.                     nack(p->name);
  1154.                     longjmp(errcatch,0);
  1155.                     /* NOTREACHED */
  1156.                 }
  1157.                 state = p->state;
  1158.                 yylval.String = p->name;
  1159.                 return (p->token);
  1160.             }
  1161.             state = CMD;
  1162.             break;
  1163.  
  1164.         case OSTR:
  1165.             if (cbuf[cpos] == '\n') {
  1166.                 state = CMD;
  1167.                 return (CRLF);
  1168.             }
  1169.             /* FALLTHROUGH */
  1170.  
  1171.         case STR1:
  1172.         case ZSTR1:
  1173.         dostr1:
  1174.             if (cbuf[cpos] == ' ') {
  1175.                 cpos++;
  1176.                 state = state == OSTR ? STR2 : ++state;
  1177.                 return (SP);
  1178.             }
  1179.             break;
  1180.  
  1181.         case ZSTR2:
  1182.             if (cbuf[cpos] == '\n') {
  1183.                 state = CMD;
  1184.                 return (CRLF);
  1185.             }
  1186.             /* FALLTHROUGH */
  1187.  
  1188.         case STR2:
  1189.             cp = &cbuf[cpos];
  1190.             n = strlen(cp);
  1191.             cpos += n - 1;
  1192.             /*
  1193.              * Make sure the string is nonempty and \n terminated.
  1194.              */
  1195.             if (n > 1 && cbuf[cpos] == '\n') {
  1196.                 cbuf[cpos] = '\0';
  1197.                 yylval.String = copy(cp);
  1198.                 cbuf[cpos] = '\n';
  1199.                 state = ARGS;
  1200.                 return (STRING);
  1201.             }
  1202.             break;
  1203.  
  1204.         case NSTR:
  1205.             if (cbuf[cpos] == ' ') {
  1206.                 cpos++;
  1207.                 return (SP);
  1208.             }
  1209.             if (isdigit(cbuf[cpos])) {
  1210.                 cp = &cbuf[cpos];
  1211.                 while (isdigit(cbuf[++cpos]))
  1212.                     ;
  1213.                 c = cbuf[cpos];
  1214.                 cbuf[cpos] = '\0';
  1215.                 yylval.Number = atoi(cp);
  1216.                 cbuf[cpos] = c;
  1217.                 state = STR1;
  1218.                 return (NUMBER);
  1219.             }
  1220.             state = STR1;
  1221.             goto dostr1;
  1222.  
  1223.         case STR3:
  1224.             if (cbuf[cpos] == ' ') {
  1225.                 cpos++;
  1226.                 return (SP);
  1227.             }
  1228.  
  1229.             cp = &cbuf[cpos];
  1230.             cp2 = strpbrk(cp, " \n");
  1231.             if (cp2 != NULL) {
  1232.                 c = *cp2;
  1233.                 *cp2 = '\0';
  1234.             }
  1235.             n = strlen(cp);
  1236.             cpos += n;
  1237.             /*
  1238.              * Make sure the string is nonempty and SP terminated.
  1239.              */
  1240.             if ((cp2 - cp) > 1) {
  1241.                 yylval.String = copy(cp);
  1242.                 cbuf[cpos] = c;
  1243.                 state = OSTR;
  1244.                 return (STRING);
  1245.             }
  1246.             break;
  1247.  
  1248.         case ARGS:
  1249.             if (isdigit(cbuf[cpos])) {
  1250.                 cp = &cbuf[cpos];
  1251.                 while (isdigit(cbuf[++cpos]))
  1252.                     ;
  1253.                 c = cbuf[cpos];
  1254.                 cbuf[cpos] = '\0';
  1255.                 yylval.Number = atoi(cp);
  1256.                 cbuf[cpos] = c;
  1257.                 return (NUMBER);
  1258.             }
  1259.             switch (cbuf[cpos++]) {
  1260.  
  1261.             case '\n':
  1262.                 state = CMD;
  1263.                 return (CRLF);
  1264.  
  1265.             case ' ':
  1266.                 return (SP);
  1267.  
  1268.             case ',':
  1269.                 return (COMMA);
  1270.  
  1271.             case 'A':
  1272.             case 'a':
  1273.                 return (A);
  1274.  
  1275.             case 'B':
  1276.             case 'b':
  1277.                 return (B);
  1278.  
  1279.             case 'C':
  1280.             case 'c':
  1281.                 return (C);
  1282.  
  1283.             case 'E':
  1284.             case 'e':
  1285.                 return (E);
  1286.  
  1287.             case 'F':
  1288.             case 'f':
  1289.                 return (F);
  1290.  
  1291.             case 'I':
  1292.             case 'i':
  1293.                 return (I);
  1294.  
  1295.             case 'L':
  1296.             case 'l':
  1297.                 return (L);
  1298.  
  1299.             case 'N':
  1300.             case 'n':
  1301.                 return (N);
  1302.  
  1303.             case 'P':
  1304.             case 'p':
  1305.                 return (P);
  1306.  
  1307.             case 'R':
  1308.             case 'r':
  1309.                 return (R);
  1310.  
  1311.             case 'S':
  1312.             case 's':
  1313.                 return (S);
  1314.  
  1315.             case 'T':
  1316.             case 't':
  1317.                 return (T);
  1318.  
  1319.             }
  1320.             break;
  1321.  
  1322.         default:
  1323.             fatal("Unknown state in scanner.");
  1324.         }
  1325.         yyerror((char *)NULL);
  1326.         state = CMD;
  1327.         longjmp(errcatch,0);
  1328.     }
  1329. }
  1330.  
  1331. upper(s)
  1332.     register char *s;
  1333. {
  1334.     while (*s != '\0') {
  1335.         if (islower(*s))
  1336.             *s = toupper(*s);
  1337.         s++;
  1338.     }
  1339. }
  1340.  
  1341. char *
  1342. copy(s)
  1343.     char *s;
  1344. {
  1345.     char *p;
  1346.  
  1347.     p = malloc((unsigned) strlen(s) + 1);
  1348.     if (p == NULL)
  1349.         fatal("Ran out of memory.");
  1350.     (void) strcpy(p, s);
  1351.     return (p);
  1352. }
  1353.  
  1354. help(ctab, s)
  1355.     struct tab *ctab;
  1356.     char *s;
  1357. {
  1358.     register struct tab *c;
  1359.     register int width, NCMDS;
  1360.     char *type;
  1361.  
  1362.     if (ctab == sitetab)
  1363.         type = "SITE ";
  1364.     else
  1365.         type = "";
  1366.     width = 0, NCMDS = 0;
  1367.     for (c = ctab; c->name != NULL; c++) {
  1368.         int len = strlen(c->name);
  1369.  
  1370.         if (len > width)
  1371.             width = len;
  1372.         NCMDS++;
  1373.     }
  1374.     width = (width + 8) &~ 7;
  1375.     if (s == 0) {
  1376.         register int i, j, w;
  1377.         int columns, lines;
  1378.  
  1379.         lreply(214, "The following %scommands are recognized %s.",
  1380.             type, "(* =>'s unimplemented)");
  1381.         columns = 76 / width;
  1382.         if (columns == 0)
  1383.             columns = 1;
  1384.         lines = (NCMDS + columns - 1) / columns;
  1385.         for (i = 0; i < lines; i++) {
  1386.             printf("   ");
  1387.             for (j = 0; j < columns; j++) {
  1388.                 c = ctab + j * lines + i;
  1389.                 printf("%s%c", c->name,
  1390.                     c->implemented ? ' ' : '*');
  1391.                 if (c + lines >= &ctab[NCMDS])
  1392.                     break;
  1393.                 w = strlen(c->name) + 1;
  1394.                 while (w < width) {
  1395.                     putchar(' ');
  1396.                     w++;
  1397.                 }
  1398.             }
  1399.             printf("\r\n");
  1400.         }
  1401.         (void) fflush(stdout);
  1402.         reply(214, "Direct comments to ftp-bugs@%s.", hostname);
  1403.         return;
  1404.     }
  1405.     upper(s);
  1406.     c = lookup(ctab, s);
  1407.     if (c == (struct tab *)NULL) {
  1408.         reply(502, "Unknown command %s.", s);
  1409.         return;
  1410.     }
  1411.     if (c->implemented)
  1412.         reply(214, "Syntax: %s%s %s", type, c->name, c->help);
  1413.     else
  1414.         reply(214, "%s%-*s\t%s; unimplemented.", type, width,
  1415.             c->name, c->help);
  1416. }
  1417.  
  1418. sizecmd(filename)
  1419. char *filename;
  1420. {
  1421.     switch (type) {
  1422.     case TYPE_L:
  1423.     case TYPE_I: {
  1424.         struct stat stbuf;
  1425.         if ((stat(filename, &stbuf) < 0 ||
  1426.             (stbuf.st_mode&S_IFMT) != S_IFREG)
  1427. #ifdef AMIGA
  1428.             && !kludge
  1429. #endif
  1430.            )
  1431.             reply(550, "%s: not a plain file.", filename);
  1432.         else
  1433.             reply(213, "%lu", stbuf.st_size);
  1434.         break;}
  1435.     case TYPE_A: {
  1436.         FILE *fin;
  1437.         register int c;
  1438.         register long count;
  1439.         struct stat stbuf;
  1440.         fin = fopen(filename, "r");
  1441.         if (fin == NULL) {
  1442.             perror_reply(550, filename);
  1443.             return;
  1444.         }
  1445.         if (fstat(fileno(fin), &stbuf) < 0 ||
  1446.             (stbuf.st_mode&S_IFMT) != S_IFREG) {
  1447.             reply(550, "%s: not a plain file.", filename);
  1448.             (void) fclose(fin);
  1449.             return;
  1450.         }
  1451.  
  1452.         count = 0;
  1453.         while((c=getc(fin)) != EOF) {
  1454.             if (c == '\n')  /* will get expanded to \r\n */
  1455.                 count++;
  1456.             count++;
  1457.         }
  1458.         (void) fclose(fin);
  1459.  
  1460.         reply(213, "%ld", count);
  1461.         break;}
  1462.     default:
  1463.         reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
  1464.     }
  1465. }
  1466.  
  1467. site_exec(cmd)
  1468. char *cmd;
  1469. {
  1470.     char buf[MAXPATHLEN];
  1471.     char *sp = (char *) strchr(cmd, ' '), *slash, *t;
  1472.     FILE *cmdf, *ftpd_popen();
  1473.  
  1474.     /* sanitize the command-string */
  1475.     
  1476.     if (sp == 0)  {
  1477.         while ((slash = strchr (cmd, '/')) != 0)
  1478.             cmd = slash + 1;
  1479.     } else {
  1480.         while (sp && (slash = (char *) strchr(cmd, '/')) 
  1481.                && (slash < sp))
  1482.             cmd = slash+1;
  1483.     }
  1484.     
  1485.     for (t = cmd;  *t && !isspace(*t);  t++) {
  1486.         if (isupper(*t)) {
  1487.             *t = tolower(*t);
  1488.         }
  1489.     }
  1490.  
  1491.     /* build the command */
  1492.     if (strlen(_PATH_EXECPATH) + strlen(cmd) + 1 > sizeof(buf))
  1493.         return;
  1494.     sprintf(buf, "%s/%s", _PATH_EXECPATH, cmd);
  1495.  
  1496.     cmdf = ftpd_popen(buf, "r", 0);
  1497.     if (!cmdf) {
  1498.         perror_reply(550, cmd);
  1499.         if (log_commands)
  1500.             syslog(LOG_INFO, "SITE EXEC (FAIL: %m): %s", cmd);
  1501.     } else {
  1502.         int lines = 0;
  1503.  
  1504.         lreply(200, cmd);
  1505.         while (fgets(buf, sizeof buf, cmdf)) {
  1506.             int len = strlen(buf);
  1507.  
  1508.             if (len>0 && buf[len-1]=='\n')
  1509.                 buf[--len] = '\0';
  1510.             lreply(200, buf);
  1511.             if (++lines >= 20) {
  1512.                 lreply(200, "*** Truncated ***");
  1513.                 break;
  1514.             }
  1515.         }
  1516.         reply(200, " (end of '%s')", cmd);
  1517.         if (log_commands)
  1518.             syslog(LOG_INFO, "SITE EXEC (lines: %d): %s", lines, cmd);
  1519.         ftpd_pclose(cmdf);
  1520.     }
  1521. }
  1522.  
  1523. alias (s)
  1524. char *s;
  1525. {
  1526.     struct aclmember *entry = NULL;
  1527.  
  1528.     if (s != (char *)NULL) {
  1529.         while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL)
  1530.             if (!strcmp(ARG0, s)) {
  1531.                 reply (214, "%s is an alias for %s.", ARG0, ARG1);
  1532.                 return;
  1533.             }
  1534.         reply (502, "Unknown alias %s.", s);
  1535.         return;
  1536.     }
  1537.  
  1538.     lreply(214, "The following aliases are available.");
  1539.  
  1540.     while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL)
  1541.         printf ("   %-8s %s\r\n", ARG0, ARG1);
  1542.     (void) fflush (stdout);
  1543.  
  1544.     reply(214, "");
  1545. }
  1546.  
  1547. cdpath ()
  1548. {
  1549.     struct aclmember *entry = NULL;
  1550.  
  1551.     lreply(214, "The cdpath is:");
  1552.     while (getaclentry("cdpath", &entry) && ARG0 != NULL)
  1553.         printf ("  %s\r\n", ARG0);
  1554.     (void) fflush (stdout);
  1555.     reply(214, "");
  1556. }
  1557.  
  1558. void
  1559. print_groups()
  1560. {
  1561. #ifdef AMIGA
  1562.   if (UserGroupBase)
  1563.   {
  1564. #endif
  1565.     gid_t  groups[NGROUPS_MAX];
  1566.     int    ngroups = 0;
  1567.  
  1568.     if ( (ngroups = getgroups(NGROUPS_MAX, groups)) < 0 ) {
  1569.         return;
  1570.     }
  1571.  
  1572.     lreply(214, "Group membership is:");
  1573.     ngroups--;
  1574.  
  1575.     for (; ngroups >= 0; ngroups--)
  1576.         lreply(214, "  %d", groups[ngroups]);
  1577.  
  1578.     (void) fflush (stdout);
  1579.     reply(214, "");
  1580. #ifdef AMIGA
  1581.   }
  1582.   else
  1583.   {
  1584.     lreply(214, "Group membership is:");
  1585.     reply(214, "");
  1586.   }
  1587. #endif
  1588. }
  1589.